golang中的map以及并发问题

您所在的位置:网站首页 golang map并发 golang中的map以及并发问题

golang中的map以及并发问题

2023-09-11 00:36| 来源: 网络整理| 查看: 265

特点 无序的键值对集合,使用key来获取,无法通过index来获取,返回顺序未知,因此每次打印的顺序可能不一样。可迭代 for range。使用hash表实现的,是引用类型。len()获取长度。key可以是所有可比较的类型:boolean, numeric, string, pointer, channel, interface, array, struct。非并发安全的。map 为引用传递。 声明与初始化 var m1 map[string]int // 只是声明,依然是 nil m2 := make(map[string]int) // 声明并创建,也就是说已经分配地址了 m3 := map[string]int{"golang":90, "python":90} m4 := map[string]int{} 如果只声明而不初始化map,那么就会创建一个nil map。将不能用来存放键值对,比如上面的m1。 fmt.Println(m1) // map[] fmt.Println(m1 == nil) // true if m1 == nil { // 空的 map m1 = make(map[string]int) } 通过key访问value,访问一个不存在的key,会返回value类型的零值,而不会报错。 因此,无法通过值来判断key是否存在,需要通过ok-idiom的方式 value, ok := map[key] if ok == true { // key是存在的 } else { // key是不存在的 } 设置值

m2[k1] = 100

如果k1不存在就是新增

删除某个key

delete(m2, k1)

k1 不存在也不会报错,并不会真的删除,只是做了一个标记而已。

遍历map for key, value := range m2 { // 顺序不是固定的 } 实现按一定顺序遍历map

把key单独抽取出来,放在数组中,对数组进行排序,然后遍历数组即可。

map的并发问题 func testmap3() { m := map[int]int{} for i := 0; i m[1] = 1 }() } }

报错:fatal error: concurrent map writes

另外如果多线程同时 read 和 write ,或者删除 key,还会出现 fatal error: concurrent map read and map write,这都是 map 存在的并发问题。

两种初始化方式的对比

var wg sync.WaitGroup var Dog map[int]int func main() { num := 10 wg.Add(num) for i := 0; i defer wg.Done() add() }() } wg.Wait() } func add() { if Dog == nil { /* 方法1 Dog = map[int]int{ 1: setV(), 2: setV(), 3: setV(), 4: setV(), 5: setV(), 6: setV(), 7: setV(), 8: setV(), 9: setV(), 10: setV(), } */ // 方法2 Dog = make(map[int]int) Dog[1] = setV() Dog[2] = setV() Dog[3] = setV() Dog[4] = setV() Dog[5] = setV() Dog[6] = setV() Dog[7] = setV() Dog[8] = setV() Dog[9] = setV() Dog[10] = setV() } else { fmt.Println(Dog[10]) } } func setV() int { return rand.Intn(10000) }

方法1 实际上是先初始化一个临时变量,最后将其赋值给 Dog,即使出现了并发,也只会是后者覆盖前者。但是方法2 会出现问题:

concurrent map read and map writeDog[10] 可能还没有被赋值。Dog 被分配了多次内存

也就说map的并发是会报错的,所以需要规避这个问题。

内置的并发安全的map数据结构 sync.Map{} func (m *Map) Delete(key interface{}) func (m *Map) Load(key interface{}) (value interface{}, ok bool) func (m *Map) LoadAndDelete(key interface{}) (value interface{}, loaded bool) func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool) func (m *Map) Range(f func(key, value interface{}) bool) func (m *Map) Store(key, value interface{}) 通过加锁 sync.Mutex sync.RWMutex sync.Once map 为引用传递

在赋值和参数传递的时候一定要注意

func main() { m := map[string]string{"name":"xxx"} t2(m) fmt.Println(m) // map[name:rao] m1 := m m["name"] = "xiao" fmt.Println(m1) // map[name:xiao] } func t2(a map[string]string) { a["name"] = "rao" }

究其原因,map的底层是指向了一个 hash map,这点与切片很像,这就导致map变量是一个假象,但是切片有 copy函数来解决这个问题,map 却没有,目前只能通过遍历然后重新赋值来实现。

map的GC回收机制

map是只增不减的一种数组结构,那些被 delete 的 key 会被打上标记,但是不会被GC回收。对于大的map对象。如果不再使用了最好使用赋值 nil 的方式使其整个的可以被GC。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3